package com.ruowei.modules.foodOrder.service;

import com.alibaba.fastjson.JSONArray;
import com.querydsl.core.BooleanBuilder;
import com.querydsl.jpa.impl.JPAQueryFactory;
import com.ruowei.modules.foodOrder.domain.*;
import com.ruowei.modules.foodOrder.domain.enumeration.OrderStatus;
import com.ruowei.modules.foodOrder.pojo.dto.*;
import com.ruowei.modules.foodOrder.repository.MerchantRepository;
import com.ruowei.modules.sys.utils.CommonUtils;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.*;

@Service
@Slf4j
public class MerchantService {
    private final QMerchant qMerchant = QMerchant.merchant;
    private final QOrder qOrder = QOrder.order;
    private final QOrderItem qOrderItem = QOrderItem.orderItem;
    private final QMerchandiseType qMerchandiseType = QMerchandiseType.merchandiseType;
    private final QMerchandise qMerchandise = QMerchandise.merchandise;
    private final QMerchandiseSku qMerchandiseSku = QMerchandiseSku.merchandiseSku;
    private final QAttributeOptions qAttributeOptions = QAttributeOptions.attributeOptions;
    private final QMerchandiseAttributes qMerchandiseAttributes = QMerchandiseAttributes.merchandiseAttributes;
    private final QSkuOptionsRelationship qSkuOptionsRelationship = QSkuOptionsRelationship.skuOptionsRelationship;
    private final JPAQueryFactory jpaQueryFactory;
    private final MerchantRepository merchantRepository;

    public MerchantService(JPAQueryFactory jpaQueryFactory, MerchantRepository merchantRepository) {
        this.jpaQueryFactory = jpaQueryFactory;
        this.merchantRepository = merchantRepository;
    }

    public SkuPriceDTO getPriceBySku(String optionCodes, String merchandiseCode) {
        Set<String> sourceCodes = new HashSet<>(Arrays.asList(optionCodes.split(",")));
        Set<String> targetCodes;
        List<MerchandiseSku> skus = jpaQueryFactory.selectFrom(qMerchandiseSku)
            .where(qMerchandiseSku.status.eq(true)
                .and(qMerchandiseSku.merchandiseCode.eq(merchandiseCode)))
            .fetch();
        SkuPriceDTO skuPriceDTO = new SkuPriceDTO(getMinimumPrice(merchandiseCode));
        for (MerchandiseSku sku : skus) {
            List<String> relationships = jpaQueryFactory.select(qSkuOptionsRelationship.optionCode)
                .from(qSkuOptionsRelationship)
                .where(qSkuOptionsRelationship.skuCode.eq(sku.getSkuCode()))
                .fetch();
            targetCodes = new HashSet<>(relationships);
            if (sourceCodes.equals(targetCodes)) {
                skuPriceDTO = new SkuPriceDTO(sku);
                return skuPriceDTO;
            }
        }
        return skuPriceDTO;
    }

    public List<MerchandiseDetailDTO> getMerchandiseDetail(String merchandiseCode) {
        List<MerchandiseSku> skus = jpaQueryFactory.selectFrom(qMerchandiseSku)
            .where(qMerchandiseSku.status.eq(true)
                .and(qMerchandiseSku.merchandiseCode.eq(merchandiseCode)))
            .fetch();
        Set<MerchandiseDetailDTO> detailDTOS = new HashSet<>();
        for (MerchandiseSku sku : skus) {
            jpaQueryFactory.select(qMerchandiseAttributes.attributeCode, qMerchandiseAttributes.attributeName)
                .from(qMerchandiseAttributes)
                .leftJoin(qAttributeOptions).on(qAttributeOptions.attributeCode.eq(qMerchandiseAttributes.attributeCode))
                .leftJoin(qSkuOptionsRelationship).on(qSkuOptionsRelationship.optionCode.eq(qAttributeOptions.optionCode))
                .where(qAttributeOptions.status.eq(true)
                    .and(qSkuOptionsRelationship.skuCode.eq(sku.getSkuCode()))
                    .and(qMerchandiseAttributes.status.eq(true)))
                .fetch()
                .forEach(tuple -> {
                    String attributeCode = tuple.get(qMerchandiseAttributes.attributeCode);
                    String attributeName = tuple.get(qMerchandiseAttributes.attributeName);
                    assert attributeCode != null;
                    List<MerchandiseDetailDTO.AttributeOption> options = new ArrayList<>();
                    jpaQueryFactory.selectFrom(qAttributeOptions)
                        .where(qAttributeOptions.status.eq(true)
                            .and(qAttributeOptions.attributeCode.eq(attributeCode)))
                        .fetch()
                        .forEach(attributeOptions -> {
                            options.add(new MerchandiseDetailDTO.AttributeOption(attributeOptions.getOptionCode(), attributeOptions.getOptionName()));
                        });
                    detailDTOS.add(new MerchandiseDetailDTO(attributeCode, attributeName, options));
                });
        }
        return new ArrayList<>(detailDTOS);
    }

    public List<AppletMerchandiseListDTO> merchandiseList(String merchantCode) {
        List<MerchandiseType> typeList = jpaQueryFactory.selectFrom(qMerchandiseType)
            .where(qMerchandiseType.status.eq(true)
                .and(qMerchandiseType.merchantCode.eq(merchantCode)))
            .orderBy(qMerchandiseType.sort.asc())
            .fetch();
        Instant now = Instant.now();
        List<AppletMerchandiseListDTO> appletMerchandiseListDTOS = new ArrayList<>();
        for (MerchandiseType type : typeList) {
            List<AppletMerchandiseDTO> appletMerchandiseDTOS = new ArrayList<>();
            List<Merchandise> merchandises = jpaQueryFactory.selectFrom(qMerchandise)
                .where(qMerchandise.deleteStatus.eq(false)
                    .and(qMerchandise.typeCode.eq(type.getTypeCode()))
                    .and(qMerchandise.merchantCode.eq(merchantCode))
                    .and(qMerchandise.publishStatus.eq(true)))
                .orderBy(qMerchandise.id.asc())
                .fetch();
            for (Merchandise merchandise : merchandises) {
                SpecificationSku specificationSku = hasSpecification(merchandise.getMerchandiseCode());
                if (specificationSku == null) {
                    continue;
                }
                long monthlySale = this.getMerchandiseMonthlySale(merchandise.getMerchandiseCode(), now);
                AppletMerchandiseDTO appletMerchandiseDTO = new AppletMerchandiseDTO(merchandise.getMerchandiseName(),
                    merchandise.getMerchandiseCode(),
                    merchandise.getPhotos(),
                    merchandise.getDescription(),
                    monthlySale,
                    getMinimumPrice(merchandise.getMerchandiseCode()).getUnitPrice(),
                    merchandise.isNewStatus(),
                    merchandise.isParticipateFullDiscount(),
                    merchandise.isRecommendStatus(),
                    getTotalSale(merchandise.getMerchandiseCode()),
                    merchandise.getUnit(),
                    merchandise.getServiceIds(),
                    merchandise.getScore(),
                    merchandise.getPraiseProportion(),
                    getParameters(merchandise.getParameters()),
                    specificationSku.getHasSpecification(),
                    specificationSku.getDefaultSkuCode());
                appletMerchandiseDTOS.add(appletMerchandiseDTO);
            }
            appletMerchandiseListDTOS.add(new AppletMerchandiseListDTO(type.getTypeName(), appletMerchandiseDTOS));
        }
        return appletMerchandiseListDTOS;
    }

    public List<MerchantDTO> getList(String merchantName) {
        BooleanBuilder builder = new BooleanBuilder();
        if (StringUtils.isNotEmpty(merchantName)) {
            builder.and(qMerchant.merchantName.contains(merchantName));
        }
        builder.and(qMerchant.status.notIn("停业", "删除"));
        List<MerchantDTO> merchantDTOS = new ArrayList<>();
        jpaQueryFactory.selectFrom(qMerchant)
            .where(builder)
            .orderBy(qMerchant.status.desc(), qMerchant.id.asc())
            .fetch()
            .forEach(merchant -> {
                long monthlySale = this.getMonthlySale(merchant.getMerchantCode(), Instant.now());
                MerchantDTO dto = new MerchantDTO(merchant.getPhotos(), merchant.getMerchantCode(), merchant.getMerchantName(), merchant.getDescription(),
                    merchant.getStartingPrice(), merchant.getStatus(), monthlySale);
                merchantDTOS.add(dto);
            });
        return merchantDTOS;
    }

    public MerchandiseSku getMinimumPrice(String merchandiseCode) {
        MerchandiseSku sku = jpaQueryFactory.selectFrom(qMerchandiseSku)
            .where(qMerchandiseSku.status.eq(true)
                .and(qMerchandiseSku.merchandiseCode.eq(merchandiseCode)))
            .orderBy(qMerchandiseSku.unitPrice.asc())
            .fetchFirst();
        if (sku == null) {
            sku = new MerchandiseSku();
            sku.setSkuCode(CommonUtils.getUUID());
            sku.setUnitPrice(BigDecimal.valueOf(10));
        }
        return sku;
    }

    public String getMerchantNameBy(String merchantCode) {
        Optional<Merchant> optionalMerchant = merchantRepository.findByMerchantCode(merchantCode);
        if (optionalMerchant.isPresent()) {
            return optionalMerchant.get().getMerchantCode();
        } else {
            return "";
        }
    }

    private String getDefaultSkuCode(String optionCode) {
        return jpaQueryFactory.select(qMerchandiseSku.skuCode)
            .from(qMerchandiseSku)
            .leftJoin(qSkuOptionsRelationship).on(qMerchandiseSku.skuCode.eq(qSkuOptionsRelationship.skuCode))
            .where(qMerchandiseSku.status.eq(true)
                .and(qSkuOptionsRelationship.optionCode.eq(optionCode)))
            .fetchFirst();
    }

    private SpecificationSku hasSpecification(String merchandiseCode) {
        List<AttributeOptions> options = jpaQueryFactory.selectFrom(qAttributeOptions)
            .leftJoin(qSkuOptionsRelationship).on(qSkuOptionsRelationship.optionCode.eq(qAttributeOptions.optionCode))
            .leftJoin(qMerchandiseSku).on(qMerchandiseSku.skuCode.eq(qSkuOptionsRelationship.skuCode))
            .where(qMerchandiseSku.merchandiseCode.eq(merchandiseCode)
                .and(qMerchandiseSku.status.eq(true))
                .and(qAttributeOptions.status.eq(true)))
            .fetch();
        SpecificationSku specificationSku = new SpecificationSku();
        if (options.size() > 1) {
            specificationSku.setHasSpecification(true);
        } else if (options.size() == 1) {
            AttributeOptions attributeOptions = options.get(0);
            if (attributeOptions.getOptionName().equals("默认")) {
                specificationSku.setHasSpecification(false);
                specificationSku.setDefaultSkuCode(getDefaultSkuCode(attributeOptions.getOptionCode()));
            } else {
                specificationSku.setHasSpecification(true);
            }
        } else {
            return null;
        }
        return specificationSku;
    }

    private List<AppletMerchandiseDTO.MerchandiseParameter> getParameters(String parameters) {
        if (StringUtils.isEmpty(parameters)) {
            return new ArrayList<>();
        }
        JSONArray jsonArray;
        try {
            jsonArray = JSONArray.parseArray(parameters);
        } catch (Exception e) {
            e.printStackTrace();
            return new ArrayList<>();
        }
        return jsonArray.toJavaList(AppletMerchandiseDTO.MerchandiseParameter.class);
    }

    private Long getMerchandiseMonthlySale(String merchandiseCode, Instant now) {
        return jpaQueryFactory.selectFrom(qOrderItem)
            .leftJoin(qOrder).on(qOrder.orderCode.eq(qOrderItem.orderCode))
            .where(qOrderItem.merchandiseCode.eq(merchandiseCode)
                .and(qOrder.deleteStatus.eq(false))
                .and(qOrder.status.eq(OrderStatus.DELIVERED))
                .and(qOrder.createTime.between(now.minus(30, ChronoUnit.DAYS), now)))
            .fetchCount();
    }

    private Long getMonthlySale(String merchantCode, Instant now) {
        return jpaQueryFactory.selectFrom(qOrderItem)
            .leftJoin(qOrder).on(qOrder.orderCode.eq(qOrderItem.orderCode))
            .where(qOrder.merchantCode.eq(merchantCode)
                .and(qOrder.deleteStatus.eq(false))
                .and(qOrder.status.eq(OrderStatus.DELIVERED))
                .and(qOrder.createTime.between(now.minus(30, ChronoUnit.DAYS), now)))
            .fetchCount();
    }

    private Long getTotalSale(String merchandiseCode) {
        return jpaQueryFactory.selectFrom(qOrderItem)
            .leftJoin(qOrder).on(qOrder.orderCode.eq(qOrderItem.orderCode))
            .where(qOrder.merchantCode.eq(merchandiseCode)
                .and(qOrder.deleteStatus.eq(false))
                .and(qOrder.status.eq(OrderStatus.DELIVERED)))
            .fetchCount();
    }

    @Data
    private static class SpecificationSku {
        private Boolean hasSpecification;
        private String defaultSkuCode;
    }
}
